/*
 * AbstractTreeModel.java
 *
 * Created on August 11, 2004, 5:36 PM
 */

package org.owasp.webscarab.util.swing;

import javax.swing.tree.TreePath;
import javax.swing.tree.TreeModel;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.EventListenerList;

/** Support for generic dynamically changing TreeModels.
 */

public abstract class AbstractTreeModel implements TreeModel {
    
    protected EventListenerList listeners;
    
    protected AbstractTreeModel() {
        listeners = new EventListenerList();
    }
    
    public int getIndexOfChild(Object parent, Object child) {
        for (int count = getChildCount(parent), i = 0; i < count; i++)
            if (getChild(parent, i).equals(child))
                return i;
        
        return -1;
    }
    
    /** Call when there is a new root, which may be null, i.e. not existent. */
    protected void fireNewRoot() {
        Object[] pairs = listeners.getListenerList();
        
        Object root = getRoot();
        
        /* Undocumented. I think it is the only reasonable/possible solution
           to use use null as path if there is no root. TreeModels without
           root aren't important anyway, since JTree doesn't support them (yet).
         */
        TreePath path = (root != null) ? new TreePath(root) : null;
        
        TreeModelEvent e = null;
        
        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == TreeModelListener.class) {
                if (e == null)
                    e = new TreeModelEvent(this, path, null, null);
                
                ((TreeModelListener)pairs[i + 1]).treeStructureChanged(e);
            }
        }
    }
    
    /** Call when everything but the root has changed. Only may be called
     * when the root is not null. Otherwise there isn't a structure to have
     * changed.
     */
    protected void fireStructureChanged() {
        fireTreeStructureChanged(new TreePath(getRoot()));
    }
    
    /** Call when a node has changed its leaf state. */
    protected void firePathLeafStateChanged(TreePath path) {
        fireTreeStructureChanged(path);
    }
    
    /** Call when the tree structure below the path has completely changed. */
    protected void fireTreeStructureChanged(TreePath parentPath) {
        Object[] pairs = listeners.getListenerList();
        
        TreeModelEvent e = null;
        
        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == TreeModelListener.class) {
                if (e == null)
                    e = new TreeModelEvent(this, parentPath, null, null);
                
                ((TreeModelListener)pairs[i + 1]).treeStructureChanged(e);
            }
        }
    }
    
    /** Call when the path itself has changed, but no structure changes
     * have occurred.
     */
    protected void firePathChanged(TreePath path) {
        Object node = path.getLastPathComponent();
        TreePath parentPath = path.getParentPath();
        
        if (parentPath == null)
            fireChildrenChanged(path, null, null);
        else {
            Object parent = parentPath.getLastPathComponent();
            
            fireChildChanged(parentPath, getIndexOfChild(parent, node), node);
        }
    }
    
    protected void fireChildAdded(TreePath parentPath, int index, Object child) {
        fireChildrenAdded(parentPath, new int[] {index}, new Object[] {child});
    }
    
    protected void fireChildChanged(TreePath parentPath, int index, Object child) {
        fireChildrenChanged(parentPath, new int[] {index}, new Object[] {child});
    }
    
    protected void fireChildRemoved(TreePath parentPath, int index, Object child) {
        fireChildrenRemoved(parentPath, new int[] {index}, new Object[] {child});
    }
    
    
    protected void fireChildrenAdded(TreePath parentPath, int[] indices, Object[] children) {
        Object[] pairs = listeners.getListenerList();
        
        TreeModelEvent e = null;
        
        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == TreeModelListener.class) {
                if (e == null)
                    e = new TreeModelEvent(this, parentPath, indices, children);
                
                ((TreeModelListener)pairs[i + 1]).treeNodesInserted(e);
            }
        }
    }
    
    protected void fireChildrenChanged(TreePath parentPath, int[] indices, Object[] children) {
        Object[] pairs = listeners.getListenerList();
        
        TreeModelEvent e = null;
        
        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == TreeModelListener.class) {
                if (e == null)
                    e = new TreeModelEvent(this, parentPath, indices, children);
                
                ((TreeModelListener)pairs[i + 1]).treeNodesChanged(e);
            }
        }
    }
    
    protected void fireChildrenRemoved(TreePath parentPath, int[] indices, Object[] children) {
        Object[] pairs = listeners.getListenerList();
        
        TreeModelEvent e = null;
        
        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == TreeModelListener.class) {
                if (e == null)
                    e = new TreeModelEvent(this, parentPath, indices, children);
                ((TreeModelListener)pairs[i + 1]).treeNodesRemoved(e);
            }
        }
    }
    
    
    protected Object clone()
    throws CloneNotSupportedException {
        AbstractTreeModel clone = (AbstractTreeModel)super.clone();
        
        clone.listeners = new EventListenerList();
        
        return clone;
    }
    
    
    
    public void addTreeModelListener(TreeModelListener l) {
        listeners.add(TreeModelListener.class, l);
    }
    
    public void removeTreeModelListener(TreeModelListener l) {
        listeners.remove(TreeModelListener.class, l);
    }
}

